/*
 * MIT License
 *
 * Copyright (c) 2023-present, tangli
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package tony.web.converter

/**
 * Enum 转换器
 *
 * 适用于基于 value 的枚举参数自动绑定（如 @RequestParam、@PathVariable）。
 * 要求枚举实现 IntEnumValue/StringEnumValue。
 * 不支持非 value 枚举的枚举。
 *
 * @author tangli
 * @date 2023/09/28 19:01
 */
import java.util.concurrent.ConcurrentHashMap
import org.springframework.core.convert.converter.Converter
import org.springframework.core.convert.converter.ConverterFactory
import tony.core.enums.EnumValue
import tony.core.enums.getEnum
import tony.core.enums.isDefaultAsNull
import tony.core.utils.asToNotNull

// 枚举类型到参数转换器的全局缓存，保证线程安全，避免重复创建 Converter 实例
private val converters = ConcurrentHashMap<Class<out EnumValue<*>>, Converter<String, *>>()

/**
 * 枚举参数转换器工厂
 * 用于将请求参数（String）自动转换为实现 EnumValue 的枚举类型
 * 缓存命中则复用 Converter，否则新建并缓存
 */
internal class EnumValueConverterFactory : ConverterFactory<String, EnumValue<*>> {
    @Suppress("UNCHECKED_CAST")
    override fun <E : EnumValue<*>> getConverter(targetType: Class<E>): Converter<String, E> =
        converters
            .getOrPut(targetType) {
                EnumValueConverter(targetType)
            }.asToNotNull()
}

/**
 * 通用枚举参数转换器
 * @param enumType 枚举类型
 * @throws IllegalArgumentException 如果 source 不能正确转换为目标 value 类型
 */
internal class EnumValueConverter(
    private val enumType: Class<out EnumValue<*>>,
) : Converter<String, EnumValue<*>> {
    /**
     * 将请求参数 source 转换为对应的枚举实例
     * @return 匹配的枚举实例，若找不到则返回 null
     * @throws IllegalArgumentException 如果 source 不能转换为目标类型
     */
    override fun convert(source: String): EnumValue<*>? {
        val enum = enumType.getEnum(source)
        return if (enum.isDefaultAsNull()) {
            null
        } else {
            enum
        }
    }
}
